#define DEBUG
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;

namespace ICSharpCode.TextEditor.Document
{
	public class SelectionManager : IDisposable
	{
		private TextLocation selectionStart;

		private IDocument document;

		private TextArea textArea;

		internal SelectFrom selectFrom = new SelectFrom();

		internal List<ISelection> selectionCollection = new List<ISelection>();

		internal TextLocation SelectionStart
		{
			get
			{
				return this.selectionStart;
			}
			set
			{
				DefaultDocument.ValidatePosition(this.document, value);
				this.selectionStart = value;
			}
		}

		public List<ISelection> SelectionCollection => this.selectionCollection;

		public bool HasSomethingSelected => this.selectionCollection.Count > 0;

		public bool SelectionIsReadonly
		{
			get
			{
				if (this.document.ReadOnly)
				{
					return true;
				}
				foreach (ISelection item in this.selectionCollection)
				{
					if (SelectionManager.SelectionIsReadOnly(this.document, item))
					{
						return true;
					}
				}
				return false;
			}
		}

		public string SelectedText
		{
			get
			{
				StringBuilder stringBuilder = new StringBuilder();
				foreach (ISelection item in this.selectionCollection)
				{
					stringBuilder.Append(item.SelectedText);
				}
				return stringBuilder.ToString();
			}
		}

		public event EventHandler SelectionChanged;

		internal static bool SelectionIsReadOnly(IDocument document, ISelection sel)
		{
			if (document.TextEditorProperties.SupportReadOnlySegments)
			{
				return document.MarkerStrategy.GetMarkers(sel.Offset, sel.Length).Exists((TextMarker m) => m.IsReadOnly);
			}
			return false;
		}

		public SelectionManager(IDocument document)
		{
			this.document = document;
			document.DocumentChanged += DocumentChanged;
		}

		public SelectionManager(IDocument document, TextArea textArea)
		{
			this.document = document;
			this.textArea = textArea;
			document.DocumentChanged += DocumentChanged;
		}

		public void Dispose()
		{
			if (this.document != null)
			{
				this.document.DocumentChanged -= DocumentChanged;
				this.document = null;
			}
		}

		private void DocumentChanged(object sender, DocumentEventArgs e)
		{
			if (e.Text == null)
			{
				this.Remove(e.Offset, e.Length);
			}
			else if (e.Length < 0)
			{
				this.Insert(e.Offset, e.Text);
			}
			else
			{
				this.Replace(e.Offset, e.Length, e.Text);
			}
		}

		public void SetSelection(ISelection selection)
		{
			if (selection != null)
			{
				if (this.SelectionCollection.Count != 1 || !(selection.StartPosition == this.SelectionCollection[0].StartPosition) || !(selection.EndPosition == this.SelectionCollection[0].EndPosition))
				{
					this.ClearWithoutUpdate();
					this.selectionCollection.Add(selection);
					this.document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, selection.StartPosition.Y, selection.EndPosition.Y));
					this.document.CommitUpdate();
					this.OnSelectionChanged(EventArgs.Empty);
				}
			}
			else
			{
				this.ClearSelection();
			}
		}

		public void SetSelection(TextLocation startPosition, TextLocation endPosition)
		{
			this.SetSelection(new DefaultSelection(this.document, startPosition, endPosition));
		}

		public bool GreaterEqPos(TextLocation p1, TextLocation p2)
		{
			return p1.Y > p2.Y || (p1.Y == p2.Y && p1.X >= p2.X);
		}

		public void ExtendSelection(TextLocation oldPosition, TextLocation newPosition)
		{
			if (oldPosition == newPosition)
			{
				return;
			}
			int x = newPosition.X;
			TextLocation textLocation;
			TextLocation textLocation2;
			if (this.GreaterEqPos(oldPosition, newPosition))
			{
				textLocation = newPosition;
				textLocation2 = oldPosition;
			}
			else
			{
				textLocation = oldPosition;
				textLocation2 = newPosition;
			}
			if (textLocation == textLocation2)
			{
				return;
			}
			if (!this.HasSomethingSelected)
			{
				this.SetSelection(new DefaultSelection(this.document, textLocation, textLocation2));
				if (this.selectFrom.where == 0)
				{
					this.SelectionStart = oldPosition;
				}
				return;
			}
			ISelection selection = this.selectionCollection[0];
			if (textLocation == textLocation2)
			{
				return;
			}
			if (this.selectFrom.where == 1)
			{
				newPosition.X = 0;
			}
			if (this.GreaterEqPos(newPosition, this.SelectionStart))
			{
				selection.StartPosition = this.SelectionStart;
				if (this.selectFrom.where == 1)
				{
					selection.EndPosition = new TextLocation(this.textArea.Caret.Column, this.textArea.Caret.Line);
				}
				else
				{
					newPosition.X = x;
					selection.EndPosition = newPosition;
				}
			}
			else
			{
				if (this.selectFrom.where == 1 && this.selectFrom.first == 1)
				{
					selection.EndPosition = this.NextValidPosition(this.SelectionStart.Y);
				}
				else
				{
					selection.EndPosition = this.SelectionStart;
				}
				selection.StartPosition = newPosition;
			}
			this.document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, textLocation.Y, textLocation2.Y));
			this.document.CommitUpdate();
			this.OnSelectionChanged(EventArgs.Empty);
		}

		public TextLocation NextValidPosition(int line)
		{
			if (line < this.document.TotalNumberOfLines - 1)
			{
				return new TextLocation(0, line + 1);
			}
			return new TextLocation(this.document.GetLineSegment(this.document.TotalNumberOfLines - 1).Length + 1, line);
		}

		private void ClearWithoutUpdate()
		{
			while (this.selectionCollection.Count > 0)
			{
				ISelection selection = this.selectionCollection[this.selectionCollection.Count - 1];
				this.selectionCollection.RemoveAt(this.selectionCollection.Count - 1);
				this.document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, selection.StartPosition.Y, selection.EndPosition.Y));
				this.OnSelectionChanged(EventArgs.Empty);
			}
		}

		public void ClearSelection()
		{
			Point mousepos = this.textArea.mousepos;
			this.selectFrom.first = this.selectFrom.where;
			TextLocation logicalPosition = this.textArea.TextView.GetLogicalPosition(mousepos.X - this.textArea.TextView.DrawingPosition.X, mousepos.Y - this.textArea.TextView.DrawingPosition.Y);
			if (this.selectFrom.where == 1)
			{
				logicalPosition.X = 0;
			}
			if (logicalPosition.Line >= this.document.TotalNumberOfLines)
			{
				logicalPosition.Line = this.document.TotalNumberOfLines - 1;
				logicalPosition.Column = this.document.GetLineSegment(this.document.TotalNumberOfLines - 1).Length;
			}
			this.SelectionStart = logicalPosition;
			this.ClearWithoutUpdate();
			this.document.CommitUpdate();
		}

		public void RemoveSelectedText()
		{
			if (this.SelectionIsReadonly)
			{
				this.ClearSelection();
				return;
			}
			List<int> list = new List<int>();
			int num = -1;
			bool flag = true;
			foreach (ISelection item in this.selectionCollection)
			{
				if (flag)
				{
					int y = item.StartPosition.Y;
					if (y != item.EndPosition.Y)
					{
						flag = false;
					}
					else
					{
						list.Add(y);
					}
				}
				num = item.Offset;
				this.document.Remove(item.Offset, item.Length);
			}
			this.ClearSelection();
			if (num >= 0)
			{
			}
			if (num == -1)
			{
				return;
			}
			if (flag)
			{
				foreach (int item2 in list)
				{
					this.document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, item2));
				}
			}
			else
			{
				this.document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
			}
			this.document.CommitUpdate();
		}

		private bool SelectionsOverlap(ISelection s1, ISelection s2)
		{
			return (s1.Offset <= s2.Offset && s2.Offset <= s1.Offset + s1.Length) || (s1.Offset <= s2.Offset + s2.Length && s2.Offset + s2.Length <= s1.Offset + s1.Length) || (s1.Offset >= s2.Offset && s1.Offset + s1.Length <= s2.Offset + s2.Length);
		}

		public bool IsSelected(int offset)
		{
			return this.GetSelectionAt(offset) != null;
		}

		public ISelection GetSelectionAt(int offset)
		{
			foreach (ISelection item in this.selectionCollection)
			{
				if (item.ContainsOffset(offset))
				{
					return item;
				}
			}
			return null;
		}

		internal void Insert(int offset, string text)
		{
		}

		internal void Remove(int offset, int length)
		{
		}

		internal void Replace(int offset, int length, string text)
		{
		}

		public ColumnRange GetSelectionAtLine(int lineNumber)
		{
			foreach (ISelection item in this.selectionCollection)
			{
				int y = item.StartPosition.Y;
				int y2 = item.EndPosition.Y;
				if (y < lineNumber && lineNumber < y2)
				{
					return ColumnRange.WholeColumn;
				}
				if (y == lineNumber)
				{
					LineSegment lineSegment = this.document.GetLineSegment(y);
					int x = item.StartPosition.X;
					int endColumn = ((y2 == lineNumber) ? item.EndPosition.X : (lineSegment.Length + 1));
					return new ColumnRange(x, endColumn);
				}
				if (y2 == lineNumber)
				{
					int x2 = item.EndPosition.X;
					return new ColumnRange(0, x2);
				}
			}
			return ColumnRange.NoColumn;
		}

		public void FireSelectionChanged()
		{
			this.OnSelectionChanged(EventArgs.Empty);
		}

		protected virtual void OnSelectionChanged(EventArgs e)
		{
			if (this.SelectionChanged != null)
			{
				this.SelectionChanged(this, e);
			}
		}
	}
}
